<!-- Copyright 2013 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -->
<!DOCTYPE html>
<title>Unit Test of e2e.BigNumModulus</title>
<script src="../../../closure/base.js"></script>
<script src="test_js_deps-runfiles.js"></script>
<script>
  goog.require('goog.testing.jsunit');
  goog.require('e2e.BigNum');
  goog.require('e2e.bigNumTestData');
  goog.require('e2e.BigNumModulus');
</script>
<script>
/**
 * Tests that number ^ prime mod prime == number.
 */
function testFermatLittleTheorem() {
  var prime1 = [
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
    0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51];
  var prime2 = [
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
  var number = [0x01, 0xFF];
  for (var i = 0; i < prime1.length - 2; i++) {
    number.push(Math.floor(Math.random() * 256));
  }
  var m1 = new e2e.BigNumModulus(prime1);
  var m2 = new e2e.BigNumModulus(prime2);

  var res1 = m1.pow(number, prime1);
  var res2 = m2.pow(number, prime2);

  assertArrayEquals(number, res1);
  assertArrayEquals(number, res2);

  // test with Uint8Arrays.
  prime1 = new Uint8Array(prime1);
  prime2 = new Uint8Array(prime2);
  number = new Uint8Array(number);

  m1 = new e2e.BigNumModulus(prime1);
  m2 = new e2e.BigNumModulus(prime2);

  res1 = m1.pow(number, prime1);
  res2 = m2.pow(number, prime2);

  assertElementsEquals(number, res1);
  assertElementsEquals(number, res2);
}


/**
 * Verify Euler's Theorem.
 */
function testEulerTheorem() {
  var n = [0x01, 0x7d, 0x24, 0x9a, 0x13, 0x08, 0x59, 0x78, 0x1c, 0xc0,
           0x2f, 0xcf, 0xb3, 0xc0, 0xdc, 0x7a, 0xe3, 0x79, 0xb2, 0xb4,
           0x59, 0xdf, 0x20, 0x07, 0xbb, 0x4d, 0x7e, 0x5e, 0xc0, 0xa6,
           0xd9, 0x06, 0x25];
  var p = [0x01, 0x3c, 0x78, 0xe2, 0x1e, 0xaa, 0xaa, 0x55, 0x78, 0xf2,
           0x1a, 0xb0, 0x32, 0xad, 0x64, 0x4f, 0x63];
  var q = [0x01, 0x34, 0x50, 0x37, 0x34, 0xfc, 0x9f, 0xea, 0x2b, 0xcb,
           0xef, 0x91, 0x3d, 0xcf, 0xfc, 0x5e, 0xd7];
  // pq mod n should be 0
  var N = new e2e.BigNumModulus(n);
  var z = N.mul(p, q);
  for (var i=0; !z[i] && i < z.length; i++);
  assertEquals("pq mod N should be 0.", z.length, i);
  var p1 = p.slice(0);
  p1.push(p1.pop() - 1);
  var q1 = q.slice(0);
  q1.push(q1.pop() - 1);
  var phiN = N.mul(p1, q1);
  var phiN1 = phiN.slice(0);
  phiN1.push(phiN1.pop() + 1);
  var one = N.pow([0x13, 0x37], phiN);
  assertArrayEquals([1], one);
  var res = N.pow([1, 2, 3, 4], phiN1);
  assertArrayEquals([1, 2, 3, 4], res);

  // test with Uint8Arrays.
  N = new e2e.BigNumModulus(new Uint8Array(n));
  z = N.mul(p, q);
  for (i=0; !z[i] && i < z.length; i++);
  assertEquals("pq mod N should be 0.", z.length, i);
  p1 = p.slice(0);
  p1.push(p1.pop() - 1);
  q1 = q.slice(0);
  q1.push(q1.pop() - 1);
  phiN = N.mul(p1, q1);
  phiN1 = phiN.slice(0);
  phiN1.push(phiN1.pop() + 1);
  one = N.pow(new Uint8Array([0x13, 0x37]), new Uint8Array(phiN));
  assertArrayEquals([1], one);
  res = N.pow(new Uint8Array([1, 2, 3, 4]), new Uint8Array(phiN1));
  assertArrayEquals([1, 2, 3, 4], res);
}


function testFromBigNum() {
  var n = [0x01, 0x7d, 0x24, 0x9a, 0x13, 0x08, 0x59, 0x78, 0x1c, 0xc0,
           0x2f, 0xcf, 0xb3, 0xc0, 0xdc, 0x7a, 0xe3, 0x79, 0xb2, 0xb4,
           0x59, 0xdf, 0x20, 0x07, 0xbb, 0x4d, 0x7e, 0x5e, 0xc0, 0xa6,
           0xd9, 0x06, 0x25];
  var one = new e2e.BigNumModulus(n);
  var two = e2e.BigNumModulus.fromBigNum(new e2e.BigNum(n));
  assertTrue(one.isEqual(two));

  // test with Uint8Arrays.
  one = new e2e.BigNumModulus(new Uint8Array(n));
  two = e2e.BigNumModulus.fromBigNum(new e2e.BigNum(new Uint8Array(n)));
  assertTrue(one.isEqual(two));
}

/**
 * Does a simple RSA encryption/decryption.
 */
function testRSA() {
  var n = [0x01, 0x7d, 0x24, 0x9a, 0x13, 0x08, 0x59, 0x78, 0x1c, 0xc0,
           0x2f, 0xcf, 0xb3, 0xc0, 0xdc, 0x7a, 0xe3, 0x79, 0xb2, 0xb4,
           0x59, 0xdf, 0x20, 0x07, 0xbb, 0x4d, 0x7e, 0x5e, 0xc0, 0xa6,
           0xd9, 0x06, 0x25];
  var e = [0x1, 0x00, 0x01];
  var d = [0xe5, 0x6a, 0xda, 0xb3, 0x0a, 0x1b, 0x90, 0x88, 0xfd, 0xc5,
           0xc9, 0xa7, 0xd1, 0x36, 0xe5, 0x5f, 0xff, 0x15, 0x5e, 0x12,
           0xe8, 0x18, 0xee, 0x4f, 0x66, 0x19, 0xe6, 0x6b, 0x12, 0x0b,
           0x36, 0x21];
  var plaintext = [0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
                   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
                   0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02];
  var N = new e2e.BigNumModulus(n);
  // encrypt / decrypt
  var ciphertext = N.pow(plaintext, e);
  assertArrayEquals(
    [0x6c,0xd2,0xfb,0xdb,0x12,0xc2,0x87,0x2e,0x4e,0xd1,0x50,0x2a,0xa5,
     0x88,0x2a,0xc5,0x48,0xd0,0x56,0x00,0x1f,0x22,0x12,0x66,0xa6,0x11,
     0xbd,0x29,0xa2,0x80,0x2a,0x85], ciphertext);
  var recovered = N.pow(ciphertext, d);
  assertArrayEquals(plaintext, recovered);

  // test with Uint8Arrays.
  var N = new e2e.BigNumModulus(new Uint8Array(n));
  var ciphertext = N.pow(new Uint8Array(plaintext), new Uint8Array(e));
  assertArrayEquals(
    [0x6c,0xd2,0xfb,0xdb,0x12,0xc2,0x87,0x2e,0x4e,0xd1,0x50,0x2a,0xa5,
     0x88,0x2a,0xc5,0x48,0xd0,0x56,0x00,0x1f,0x22,0x12,0x66,0xa6,0x11,
     0xbd,0x29,0xa2,0x80,0x2a,0x85], ciphertext);
  var recovered = N.pow(new Uint8Array(ciphertext), new Uint8Array(d));
  assertArrayEquals(plaintext, recovered);
}


</script>
